<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div class="box">
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
  </div>
  <script>
    // 1.1 扩展运算符：将数组转为用逗号分隔的参数序列
    console.log(...[1,2,3]);
    console.log(...[1,2,3], 4, 5);
    // 1.2 扩展运算符可以放到函数的参数调用里面
    function add(a, b) {
      return a + b
    }
    let arr = [3, 4]
    console.log(add(...arr), 'add(...arr)'); // 7
    // 1.3 扩展运算符后面可以跟表达式
    let x = 7;
    const arr2 = [
      ...(x > 0 ? ['a'] : []),
      'b'
    ]
    console.log(arr2, 'arr2'); // ['a', 'b']

    // 扩展运算符在函数调用时 才可以放到括号里面
    // console.log((...[1,2])); // 会报错
    console.log(...[1, 2]); // log本身就是函数

    // 1.4 扩展运算符可以替换apply方法
    // es5
    let arr3 = [3,4,5]
    console.log(Math.max.apply(null, arr3), 'Math.max.apply(null, arr3)'); // 5
    console.log(Math.max(...arr3)); // 5 这是简写

    // 1.5 将一个数组添加到数组的尾部
    let arr4 = [1,2,3]
    let arr5 = [2,3,4]
    Array.prototype.push.apply(arr4, arr5)
    console.log(arr4, 'arr4'); // [1,2,3,2,3,4]
    let arr6 = [1,2,3]
    arr6.push(...arr5)
    console.log(arr6, 'arr6');

    // Date() 日期的生成
    console.log(new Date(...[2016, 3, 3]), '[2015, 1, 1]');
    /* ------------------------------------------------------------------------------------------ */

    // 1. 复制数组
    const a1 = [3,3,3,4]
    const a2 = a1.concat()
    a2[0] = 333
    console.log(a2, 'a2');
    console.log(a1, 'a1'); // a1并未受到影响

    const a3 = [...a1]
    a3[2] = 333
    console.log(a3, 'a3');
    console.log(a1, 'a1'); // 并未受到影响

    const [...a4]  = a1
    console.log(a4, 'a4'); // 这样也是对a1的拷贝

    
    // 2. 合并数组,是浅拷贝，注意


    // 3. 可以和解构赋值结合起来
    const [first, ...rest] = [1,2,3,4,5]
    console.log(first, 'first'); // 1
    console.log(rest, 'rest'); // [2,3,4,5]
    // const [..res1, sec] = [1,2,2,2,2,2,23,4] // 报错 应该放在最后一位

    // 4. 字符
    console.log([...'hello'], 'hello'); // [h e l l o]
    console.log('x\uD83D\uDE80y'.length, 'length'); // 会将4个字节的unicode字符识别为2个
    console.log([...'x\uD83D\uDE80y'].length); // 3 他是识别正常的

    function length(str) {
      return [...str].length
    }
    console.log(length('x\uD83D\uDE80y'), 'length x\uD83D\uDE80y'); // 3

    let str = 'x\uD83D\uDE80y';
    console.log(str, 'str');
    console.log(str.split('').reverse().join(''), "str.split('').reverse().join('')"); 
    // 'y\uDE80\uD83Dx' 不用扩展运算符，这里的reverse就是错误的，

    console.log([...str].reverse().join(''), "[...str].reverse().join('')");
    // 'y\uD83D\uDE80x' 用了扩展运算符，这里的reverse就是正确的

    // 5. 实现了Iterator接口的对象
    // 任何定义了Iterator接口的对象，对可以运用扩展运算符转为真正的数组
    // NodeList对象里面有Iterator接口，字符串，map，set都有迭代器，对象无
    let box = document.querySelectorAll('.box span')
    console.log(box, 'box');
    console.log([...box], 'box arr');

    let obj = {
      '0': 'a',
      '1': 'b',
      '2': 'c',
      length: 3
    }
    console.log(obj, 'obj');
    // console.log([...obj], 'obj arr');  // TypeError: obj is not iterable，obj是类似数组，但是就是没有Iterator接口
    console.log([...Array.from(obj)], 'Array.from(obj)');

    Number.prototype[Symbol.iterator] = function * () {
      let i = 0
      let num  = this.valueOf()
      // num拿到的是5，i < 5，会返回从 0 到 5的数，
      while (i < num) {
        yield i++ // 返回对应的i
      }
    }
    console.log(...5); // 0,1,2,3,4
    console.log([...5]); // [0,1,2,3,4]
    console.log(new Set([[1,2,3]])); // set有迭代器
    console.log(new Map([[1,2,3]])); // map也有迭代器
    console.log(new String('123')); // 字符串也有迭代器
  </script>
</body>
</html>